﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Autofac;
using Common.Logging;
using Common.Logging.Configuration;
using Common.Logging.Log4Net;
using Inovout.Component;
using Inovout.Data;
using Inovout.Runtime;
using Microsoft.Owin;
using Microsoft.Owin.BuilderProperties;
using Microsoft.Owin.Security.OAuth;
using Owin;

namespace Inovout.Web
{
    public abstract class WebApplication : IApplication
    {
        private ILog log;
        protected WebApplication()
        {
            log = LogManager.GetLogger(this.GetType());
            applicationConfig = ApplicationProperties.GetProperties();
        }
        protected virtual void ConfigurationApplication(IAppBuilder app)
        {
            if (ComponentRegistry.IsRegistered<IApplicationConfiguration>())
            {
                var applicationConfiguration = ComponentRegistry.Resolve<IApplicationConfiguration>();
                applicationConfiguration.Configure(app);
            }
        }

        public void Configuration(IAppBuilder app)
        {
            ConfigureLogging();
            var builder = new ContainerBuilder();

            builder.RegisterInstance<IApplication>(this).SingleInstance();

            Assembly[] moduleAssemblies = GetMoudleAessemblies(app);

            builder.RegisterCoreComponents(moduleAssemblies);
            builder.RegisterComponents(moduleAssemblies);

            RegisterDataComponents(builder, moduleAssemblies);
            RegisterWebComponents(builder, moduleAssemblies);

            IContainer container = builder.Build();
            ComponentRegistry.Initialize(container);

            Configuration(container, app);
            ConfigurationApplication(app);
            ConfiguationAuthentication(app);

            RegisterDependencyResolver(container);
            RegisterRoutes();
            RegisterFilters();
        }
        private void ConfigureLogging()
        {
            NameValueCollection nvc = new NameValueCollection();
            nvc.Add("configType", "FILE");

            if (log.IsDebugEnabled)
            {
                nvc.Add("configFile", "~/Configs/log4net.debug.config");

            }
            else
            {
                nvc.Add("configFile", "~/Configs/log4net.config");
            }

            LogManager.Adapter = new Log4NetLoggerFactoryAdapter(nvc);
            if (log4net.LogManager.GetAllRepositories().Length == 0)
            {
                log.Warn("log4net failure");
            }
            else
            {
                log = LogManager.GetLogger(this.GetType());
                log.Info("log4net success");
            }
        }
        private readonly Dictionary<string, string> applicationConfig;

        private static readonly Uri binUri = new Uri(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase));

        public IDictionary<string, string> Properties { get { return applicationConfig; } }
        private static readonly string AppOrganizationKey = "app.org";
        private Assembly[] GetMoudleAessemblies(IAppBuilder app)
        {
            string appOrganization = null;
            if (!applicationConfig.TryGetValue(AppOrganizationKey, out appOrganization))
            {
                AppProperties appProperties = new AppProperties(app.Properties);
                appOrganization = appProperties.AppName;
                if (appOrganization.Equals(this.GetType().AssemblyQualifiedName))
                {
                    appOrganization = Assembly.GetEntryAssembly().GetName().Name;
                }
                appOrganization = appOrganization.Substring(0, appProperties.AppName.IndexOf("."));
            }
            var moduleDllFiles = Directory.GetFiles(binUri.LocalPath, "inovout.*.dll")
                .Union(Directory.GetFiles(binUri.LocalPath, appOrganization + ".*.dll"));


            ISet<Assembly> assemblies = new HashSet<Assembly>();
            foreach (string moduleFile in moduleDllFiles)
            {
                string assemblyNameString = Path.GetFileNameWithoutExtension(moduleFile);
                assemblies.Add(Assembly.Load(assemblyNameString));
            }
            if (Assembly.GetEntryAssembly() != null)
            {
                //EXE也是Moduel Assembly
                assemblies.Add(Assembly.GetEntryAssembly());
            }
            return assemblies.ToArray();
        }

        private void Configuration(ILifetimeScope container, IAppBuilder app)
        {
            app.UseAutofacMiddleware(container);
        }
        protected virtual void ConfiguationAuthentication(IAppBuilder app)
        {
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }

        protected virtual void RegisterDataComponents(ContainerBuilder builder, Assembly[] moduleAssemblies)
        {
            builder.RegisterDomains(moduleAssemblies);
            builder.RegisterRepositories(moduleAssemblies);
        }

        protected virtual void RegisterWebComponents(ContainerBuilder builder, Assembly[] moduleAssemblies)
        {
            builder.RegisterAssemblyTypes(moduleAssemblies)
                 .Where(t => typeof(IApplicationConfiguration).IsAssignableFrom(t))
                 .As<IApplicationConfiguration>();
        }
        protected abstract void RegisterDependencyResolver(ILifetimeScope container);
        protected abstract void RegisterRoutes();
        protected abstract void RegisterFilters();

    }
}
